import Vue from 'vue'
import $web3js from '../web3.js';

const erc20TokenContract = require('./erc20TokenContract.json')

let web3 = $web3js.getWeb3();
let contractMap = new Map();
let decimalsMap = new Map();
let symbolMap = new Map();
let nameMap = new Map();
let totalSupplyMap = new Map();

const handleContractEmpty = (_defaultValue) => {
  return new Promise((resolve, reject) => {
    resolve(_defaultValue);
  })
}

const getErc20TokenContract = (_contractAddress) => {
  let _erc20Contract = contractMap.get(_contractAddress);
  if (!_erc20Contract) {
    try {
      _erc20Contract = web3 ? new web3.eth.Contract(erc20TokenContract.abi, _contractAddress) : '';
    } catch (error) {
      Vue.prototype.$toast(error && error.message ? error.message : error);
    }
    if (!!_erc20Contract) {
      contractMap.set(_contractAddress, _erc20Contract)
    }
  }
  return _erc20Contract;
}

const fromErc20TokenValue = async (_value, _contractAddress) => {
  let _decimals = await getErc20TokenDecimals(_contractAddress);
  return await Vue.prototype.$numberUtils.movePointLeft(_value, _decimals);
}

const toErc20TokenValue = async (_value, _contractAddress) => {
  let _decimals = await getErc20TokenDecimals(_contractAddress);
  return Vue.prototype.$numberUtils.movePointRight(_value, _decimals);
}

const getErc20TokenName = (_contractAddress) => {
  let _name = nameMap.get(_contractAddress);
  if (!_name) {
    let contract = getErc20TokenContract(_contractAddress);
    if (!!contract) {
      _name = $web3js.commonCall(contract.methods.name);
      if (!!_name) {
        nameMap.set(_contractAddress, _name)
      }
    } else {
      return handleContractEmpty("");
    }
  }
  return _name;
}

const getErc20TokenSymbol = (_contractAddress) => {
  let _symbol = symbolMap.get(_contractAddress);
  if (!_symbol) {
    let contract = getErc20TokenContract(_contractAddress);
    if (!!contract) {
      _symbol = $web3js.commonCall(contract.methods.symbol);
      if (!!_symbol) {
        symbolMap.set(_contractAddress, _symbol)
      }
    } else {
      return handleContractEmpty("");
    }
  }
  return _symbol;
}

const getErc20TokenDecimals = (_contractAddress) => {
  let _decimals = decimalsMap.get(_contractAddress);
  if (!_decimals) {
    let contract = getErc20TokenContract(_contractAddress);
    if (!!contract) {
      _decimals = $web3js.commonCall(contract.methods.decimals);
      if (!!_decimals) {
        decimalsMap.set(_contractAddress, _decimals)
      }
    } else {
      return handleContractEmpty(null);
    }
  }
  return _decimals;
}

const getErc20TokenTotalSupply = async (_contractAddress, reLoad) => {
  let _totalSupply = totalSupplyMap.get(_contractAddress);
  if (!_totalSupply || !!reLoad) {
    let contract = getErc20TokenContract(_contractAddress);
    if (!!contract) {
      _totalSupply = $web3js.commonCallAndHandlerParams(contract.methods.totalSupply, fromErc20TokenValue, [_contractAddress]);
      if (!!_totalSupply) {
        totalSupplyMap.set(_contractAddress, _totalSupply)
      }
    } else {
      return handleContractEmpty(null);
    }
  }
  return _totalSupply;
}

const getErc20TokenBalance = async (_contractAddress, _owner) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  return $web3js.commonCallAndHandlerParams(contract.methods.balanceOf, fromErc20TokenValue, [_contractAddress], _owner);
}

const getErc20TokenAllowance = async (_contractAddress, owner, spender) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  return $web3js.commonCallAndHandlerParams(contract.methods.allowance, fromErc20TokenValue, [_contractAddress], owner, spender);
}

const erc20TokenApprove = async (_contractAddress, _spender, _value) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  _value = await toErc20TokenValue(_value, _contractAddress);
  return $web3js.commonSend(contract.methods.approve, _spender, _value);
}

const erc20TokenApproveEstimateGas = async (_contractAddress, _spender, _value) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  _value = await toErc20TokenValue(_value, _contractAddress);
  return $web3js.commonEstimateGas(contract.methods.approve, _spender, _value);
}

const erc20TokenTransfer = async (_contractAddress, _to, _value) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  _value = await toErc20TokenValue(_value, _contractAddress);
  return $web3js.commonSend(contract.methods.transfer, _to, _value);
}

const erc20TokenTransferEstimateGas = async (_contractAddress, _to, _value) => {
  let contract = getErc20TokenContract(_contractAddress);
  if (!contract) {
    return handleContractEmpty("");
  }
  _value = await toErc20TokenValue(_value, _contractAddress);
  return $web3js.commonEstimateGas(contract.methods.approve, _to, _value);
}


export default {
  fromErc20TokenValue,
  toErc20TokenValue,
  getErc20TokenContract,
  getErc20TokenName,
  getErc20TokenSymbol,
  getErc20TokenDecimals,
  getErc20TokenTotalSupply,
  getErc20TokenBalance,
  getErc20TokenAllowance,
  erc20TokenApprove,
  erc20TokenApproveEstimateGas,
  erc20TokenTransfer,
  erc20TokenTransferEstimateGas
}